CIFileDialogEvents and CIFileDialogControlEvents classes.

The CIFileDialogEents class implements the IFileDialogEvents interface, that exposes methods that allow notification of events within a common file dialog.

The CIFileDialogControlEvents class implements the IFileDialogControlEvents interface, that exposes methods that allow an application to be notified of events that are related to controls that the application has added to a common file dialog.

Include file: AfxNova/CIFileDialogEvents.inc


CIFileDialogEents Methods

Name Description
OnFileOk Called just before the dialog is about to return with a result.
OnFolderChange Called when the user navigates to a new folder.
OnFolderChanging Called before IFileDialogEvents::OnFolderChange. This allows the implementer to stop navigation to a particular location.
OnOverwrite Called from the save dialog when the user chooses to overwrite a file.
OnSelectionChange Called when the user changes the selection in the dialog’s view.
OnShareViolation Enables an application to respond to sharing violations that arise from Open or Save operations.
OnTypeChange Called when the dialog is opened to notify the application of the initial chosen filetype.

CIFileDialogControlEvents Methods

Name Description
OnButtonClicked Called when the user clicks a command button.
OnCheckButtonToggled Called when the user changes the state of a check button (check box).
OnControlActivating Called when an Open button drop-down list customized through EnableOpenDropDown or a Tools menu is about to display its contents.
OnItemSelected Called when an item is selected in a combo box, when a user clicks an option button (also known as a radio button), or an item is chosen from the Tools menu.

Template code

Name Description
Template Template code that implements the FileDialogEvents callback interface.
Template2 Template code that implements the FileDialogEvents and IFileDialogControlEvents callback interfaces.

OnFileOk

Called just before the dialog is about to return with a result.

FUNCTION OnFileOk (BYVAL pfd AS IFileDialog PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.

Implementations should return S_OK to accept the current result in the dialog or S_FALSE to refuse it. In the case of S_FALSE, the dialog should remain open.

Remarks

When this method is called, the GetResult, GetResultString and GetResults methods can be called.

The application can use this callback method to perform additional validation before the dialog closes, or to prevent the dialog from closing. If the application prevents the dialog from closing, it should display a UI to indicate a cause. To obtain a parent HWND for the UI, obtain the IOleWindow interface through IFileDialog.QueryInterface and call IOleWindow.GetWindow.

An application can also use this method to perform all of its work surrounding the opening or saving of files.


OnFolderChange

Called when the user navigates to a new folder.

FUNCTION OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.

Return value

Returns S_OK if successful, or an error value otherwise.

Remarks

OnFolderChange is called when the dialog is opened.


OnFolderChanging

Called before OnFolderChange. This allows the implementer to stop navigation to a particular location.

FUNCTION OnFolderChanging (BYVAL pfd AS IFileDialog PTR, BYVAL psiFolder AS IShellItem PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.
psiFolder A pointer to an interface that represents the folder to which the dialog is about to navigate.

Return value

Returns S_OK if successful, or an error value otherwise. A return value of S_OK or E_NOTIMPL indicates that the folder change can proceed.


OnOverwrite

Called from the save dialog when the user chooses to overwrite a file.

FUNCTION OnOverwrite (BYVAL pfd AS IFileDialog PTR, BYVAL psi AS IShellItem PTR, _
   BYVAL pResponse AS FDE_OVERWRITE_RESPONSE PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.
psi A pointer to the interface that represents the item that will be overwritten.
pResponse A pointer to a value from the FDE_OVERWRITE_RESPONSE enumeration indicating the response to the potential overwrite action.

FDE_OVERWRITE_RESPONSE enumeration

Flag Value Description
FDEOR_DEFAULT 0 The application has not handled the event. The dialog displays a UI asking the user whether the file should be overwritten and returned from the dialog.
FDEOR_ACCEPT 1 The application has determined that the file should be returned from the dialog.
FDEOR_REFUSE 2 The application has determined that the file should not be returned from the dialog.

Return value

The implementer should return E_NOTIMPL if this method is not implemented; S_OK or an appropriate error code otherwise.

Remarks

The FOS_OVERWRITEPROMPT flag must be set through IFileDialog.SetOptions before this method is called.


OnSelectionChange

Called when the user changes the selection in the dialog’s view.

FUNCTION OnSelectionChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.

Return value

If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.


OnShareViolation

Enables an application to respond to sharing violations that arise from Open or Save operations.

FUNCTION OnShareViolation (BYVAL pfd AS IFileDialog PTR, BYVAL psi AS IShellItem PTR, _
   BYVAL pResponse AS FDE_SHAREVIOLATION_RESPONSE PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.
psi A pointer to the interface that represents the item that has the sharing violation.
pResponse A pointer to a value from the FDE_SHAREVIOLATION_RESPONSE enumeration indicating the response to the sharing violation.

FDE_SHAREVIOLATION_RESPONSE enumeration

Flag Value Description
FDESVR_DEFAULT 0 The application has not handled the event. The dialog displays a UI that indicates that the file is in use and a different file must be chosen.
FDESVR_ACCEPT 1 The application has determined that the file should be returned from the dialog.
FDESVR_REFUSE 2 The application has determined that the file should not be returned from the dialog.

Return value

The implementer should return E_NOTIMPL if this method is not implemented; S_OK or an appropriate error code otherwise.

Remarks

The FOS_SHAREAWARE flag must be set through IFileDialog.SetOptions before this method is called.

A sharing violation could possibly arise when the application attempts to open a file, because the file could have been locked between the time that the dialog tested it and the application opened it.


OnTypeChange

Called when the dialog is opened to notify the application of the initial chosen filetype.

FUNCTION OnTypeChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
Parameter Description
pfd A pointer to the interface that represents the dialog.

Return value

Returns S_OK if successful, or an error value otherwise.


OnButtonClicked

Called when the user clicks a command button.

FUNCTION OnButtonClicked (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD) AS HRESULT
Parameter Description
pfdc A pointer to the interface through which the application added controls to the dialog.
dwIDCtl The ID of the button that the user clicked.

Return value

If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.


OnCheckButtonToggled

Called when the user changes the state of a check button (check box).

FUNCTION OnCheckButtonToggled (BYVAL pfdc AS IFileDialogCustomize PTR, _
   BYVAL dwIDCtl AS DWORD, BYVAL bChecked AS WINBOOL) AS HRESULT
Parameter Description
pfdc A pointer to the interface through which the application added controls to the dialog.
dwIDCtl The ID of the button that the user clicked.
bChecked A BOOL indicating the current state of the check button. TRUE if checked; FALSE otherwise.

Return value

If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.


OnControlActivating

Called when an Open button drop-down list customized through EnableOpenDropDown or a Tools menu is about to display its contents.

FUNCTION OnControlActivating (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD) AS HRESULT
Parameter Description
pfdc A pointer to the interface through which the application added controls to the dialog.
dwIDCtl The ID of the list or menu about to display.

Return value

If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.


Template

The following template implements the FileDialogEvents callback interface.

' ########################################################################################
' CIFileDialogEvents class
' Implementation of the FileDialogEvents callback interface
' ########################################################################################
TYPE CIFileDialogEvents EXTENDS OBJECT

   DECLARE VIRTUAL FUNCTION QueryInterface (BYVAL riid AS REFIID, BYVAL ppvObject AS LPVOID PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION AddRef () AS ULONG
   DECLARE VIRTUAL FUNCTION Release () AS ULONG
   DECLARE VIRTUAL FUNCTION OnFileOk (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION OnFolderChanging (BYVAL pfd AS IFileDialog PTR, BYVAL psiFolder AS IShellItem PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION OnSelectionChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION OnShareViolation (BYVAL pfd AS IFileDialog PTR, BYVAL psi AS IShellItem PTR, BYVAL pResponse AS FDE_SHAREVIOLATION_RESPONSE PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION OnTypeChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   DECLARE VIRTUAL FUNCTION OnOverwrite (BYVAL pfd AS IFileDialog PTR, BYVAL psi AS IShellItem PTR, BYVAL pResponse AS FDE_OVERWRITE_RESPONSE PTR) AS HRESULT

   DECLARE CONSTRUCTOR
   DECLARE DESTRUCTOR

   ' Reference count for COM
   refCount AS ULONG = 0

END TYPE
' ########################################################################################

' ########################################################################################
' Template example to set a IFileDialogEvents innterface.
' ########################################################################################

' =====================================================================================
' Constructor
' =====================================================================================
PRIVATE CONSTRUCTOR CIFileDialogEvents
END CONSTRUCTOR
' =====================================================================================
' =====================================================================================
' Destructor
' =====================================================================================
PRIVATE DESTRUCTOR CIFileDialogEvents
END DESTRUCTOR
' =====================================================================================

' *** IUnknown interface methods ***

' ========================================================================================
' Returns pointers to the implemented classes and supported interfaces.
' ========================================================================================
PRIVATE FUNCTION CIFileDialogEvents.QueryInterface (BYVAL riid AS REFIID, BYVAL ppvObj AS LPVOID PTR) AS HRESULT
   COSFD_DP("riid: " & AfxGuidText(riid))
   IF ppvObj = NULL THEN RETURN E_INVALIDARG
   IF IsEqualIID(riid, @IID_IFileOpenDialog) OR _
      IsEqualIID(riid, @IID_IFileSaveDialog) OR _
      IsEqualIID(riid, @IID_IUnknown) THEN
      *ppvObj = @this
      ' // Increment the reference count
      this.AddRef
      RETURN S_OK
   ELSE
      IF IsEqualIID(riid, @IID_IFileDialogControlEvents) THEN
         DIM pIFileDialogControlEvents AS CIFileDialogControlEvents PTR = NEW CIFileDialogControlEvents
         pIFileDialogControlEvents->AddRef
         *ppvObj = CAST(ANY PTR, pIFileDialogControlEvents)
         RETURN S_OK
      END IF
   END IF
   RETURN E_NOINTERFACE
END FUNCTION
' =====================================================================================

' ========================================================================================
' Increments the reference count for an interface on an object. This method should be called
' for every new copy of a pointer to an interface on an object.
' ========================================================================================
PRIVATE FUNCTION CIFileDialogEvents.AddRef () AS ULONG
   refCount += 1
   OutputDebugStringW("CIFileDialogEvents.AddRef " & ..WSTR(refCount))
   RETURN refCount
END FUNCTION
' ========================================================================================

' ========================================================================================
' Decrements the reference count for an interface on an object.
' If the count reaches 0, it deletes itself.
' ========================================================================================
PRIVATE FUNCTION CIFileDialogEvents.Release () AS ULONG
   refCount -= 1
   IF refCount = 0 THEN
      Delete @this
   END IF
   RETURN refCount
END FUNCTION
' =====================================================================================

' *** Event Handlers ***

' =====================================================================================
' Called just before the dialog is about to return with a result.
' When this method is called, the IFileDialog::GetResult and GetResults methods can be called.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnFileOk (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' =====================================================================================

' =====================================================================================
' Called before OnFolderChange. This allows the implementer to stop navigation to a particular location.
' A pointer to an interface that represents the folder to which the dialog is about to navigate.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnFolderChanging (BYVAL pfd AS IFileDialog PTR, _
   BYVAL psiFolder AS IShellItem PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' =====================================================================================

' =====================================================================================
' Called when the user navigates to a new folder.
' OnFolderChange is called when the dialog is opened.
' The code below allows to set the position of the dialog, but can't center it because
' the width and height of the dialog isn't settled until it is displayed.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT

'   ' // Before displaying the dialog get its coordinates
'   DIM pOleWindow AS IOleWindow PTR
'   ' // Get a pointer to the IOleWindow interface
'   pfd->lpvtbl->QueryInterface(pfd, @IID_IOleWindow, @pOleWindow)
'   ' // Get the window handle of the dialog
'   DIM hOleWindow AS HWND
'   IF pOleWindow THEN pOleWindow->lpvtbl->GetWindow(pOleWindow, @hOleWindow)
'   OutputDebugStringW("CIFileDialogEvents.OnFolderChange - hOleWindow: " & ..WSTR(hOleWindow))
'   IF hOleWindow THEN
'      ' // Get he bounding rectangle of the parent window
'      DIM AS RECT rcDlg, rcOwner
'      GetWindowRect(GetParent(hOleWindow), @rcOwner)
'      ' // Get he bounding rectangle of the open file dialog
'      GetWindowRect(hOleWindow, @rcDlg)
'      ' // Maps the open file dialog coordinates relative to its parent window
'      MapWindowPoints(GetParent(hOleWindow), hOleWindow, CAST(POINT PTR, @rcDlg), 2)
'   END IF

   RETURN S_OK

END FUNCTION
' =====================================================================================

' =====================================================================================
' Called when the user changes the selection in the dialog's view.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnSelectionChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' =====================================================================================

' =====================================================================================
' Enables an application to respond to sharing violations that arise from Open or Save operations.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnShareViolation (BYVAL pfd AS IFileDialog PTR, _
   BYVAL psi AS IShellItem PTR, BYVAL pResponse AS FDE_SHAREVIOLATION_RESPONSE PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' =====================================================================================

' =====================================================================================
' Called when the dialog is opened to notify the application of the initial chosen filetype.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnTypeChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' =====================================================================================

' =====================================================================================
' Called from the save dialog when the user chooses to overwrite a file.
' =====================================================================================
PRIVATE FUNCTION CIFileDialogEvents.OnOverwrite (BYVAL pfd AS IFileDialog PTR, _
   BYVAL psi AS IShellItem PTR, BYVAL pResponse AS FDE_OVERWRITE_RESPONSE PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' =====================================================================================

Example of how to override the template with yur own class:

' ########################################################################################
' CIFileDialogEventsImpl class
' Implementation of the FileDialogEvents callback interface
' ########################################################################################
TYPE CIFileDialogEventsImpl EXTENDS CIFileDialogEvents

   DECLARE FUNCTION OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT

END TYPE

PRIVATE FUNCTION CIFileDialogEventsImpl.OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT

   ' // Before displaying the dialog get its coordinates
   DIM pOleWindow AS IOleWindow PTR
   ' // Get a pointer to the IOleWindow interface
   pfd->lpvtbl->QueryInterface(pfd, @IID_IOleWindow, @pOleWindow)
   ' // Get the window handle of the dialog
   DIM hOleWindow AS HWND
   IF pOleWindow THEN pOleWindow->lpvtbl->GetWindow(pOleWindow, @hOleWindow)
   OutputDebugStringW ("CIFileDialogEventsImpl.OnFolderChange - hOleWindow: " & ..WSTR(hOleWindow))
   IF hOleWindow THEN
      ' // Get he bounding rectangle of the parent window
      DIM AS RECT rcDlg, rcOwner
      GetWindowRect(GetParent(hOleWindow), @rcOwner)
      ' // Get he bounding rectangle of the open file dialog
      GetWindowRect(hOleWindow, @rcDlg)
      ' // Maps the open file dialog coordinates relative to its parent window
      MapWindowPoints(GetParent(hOleWindow), hOleWindow, CAST(POINT PTR, @rcDlg), 2)
   END IF

   RETURN S_OK
END FUNCTION
' ========================================================================================

Set the events to your class with the SetEvents method:

' // Set events
DIM pfde AS ANY PTR = NEW CIFileDialogEventsImpl
pofd.SetEvents(pfde)
DIM pofd AS CIOpenFileDialog
' // Set the file types
pofd.AddFileType("FB code files", "*.bas;*.inc;*.bi")
pofd.AddFileType("Executable files", "*.exe;*.dll")
pofd.AddFileType("All files", "*.*")
pofd.SetFileTypes()
' // Multiple selection (default is single selection)
DIM options AS FILEOPENDIALOGOPTIONS = pofd.GetOptions
pofd.SetOptions(options OR FOS_ALLOWMULTISELECT)
' // Optional: Set the title of the dialog
'   pofd.SetTitle("A Single-Selection Dialog")
' // Set the folder
pofd.SetFolder(CURDIR)
pofd.SetDefaultExtension("bas")
pofd.SetFileTypeIndex(1)
' // Set events
DIM pfde AS ANY PTR = NEW CIFileDialogEventsImpl
pofd.SetEvents(pfde)
' // Display the dialog
DIM hr AS HRESULT = pofd.ShowOpen(hwnd)
' // Folder name
OutputDebugStringW("Folder name: " & pofd.GetFolder)
' *** Single selection ***
' // Get the result
'IF hr = S_OK THEN
'   print pofd.GetResultString
'END IF
' *** Multiple selection ***
IF hr = S_OK THEN
   DIM dwsRes AS DWSTRING = pofd.GetResultsString
   FOR i AS LONG = 1 TO pofd.GetResultsCount
      OutputDebugStringW pofd.ParseResults(dwsRes, i)
   NEXT
END IF

You can use the same class of the Save file dialog:

' ########################################################################################
' CIFileDialogEventsImpl class
' Implementation of the FileDialogEvents callback interface
' ########################################################################################
TYPE CIFileDialogEventsImpl EXTENDS CIFileDialogEvents

   DECLARE FUNCTION OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   DECLARE FUNCTION OnFileOk (BYVAL pfd AS IFileDialog PTR) AS HRESULT

END TYPE

PRIVATE FUNCTION CIFileDialogEventsImpl.OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   RETURN S_OK
END FUNCTION

PRIVATE FUNCTION CIFileDialogEventsImpl.OnFileOk (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   RETURN S_OK
END FUNCTION
' ########################################################################################

Setting the event’s handler:

DIM psfd AS CISaveFileDialog
' // Set the file types
psfd.AddFileType("FB code files", "*.bas;*.inc;*.bi")
psfd.AddFileType("Executable files", "*.exe;*.dll")
psfd.AddFileType("All files", "*.*")
psfd.SetFileTypes()
' // Optional: Set the title of the dialog
'   psfd.SetTitle("Save File Dialog")
' // Set the folder
psfd.SetFolder(CURDIR)
psfd.SetDefaultExtension("bas")
psfd.SetFileTypeIndex(1)
' // Set events
DIM pfde AS ANY PTR = NEW CIFileDialogEventsImpl
psfd.SetEvents(pfde)
' // Display the dialog
DIM hr AS HRESULT = psfd.ShowSave(hwnd)
' // Folder name
OutputDebugStringW("Folder name: " & psfd.GetFolder)
' // Get the result
IF hr = S_OK THEN
   OutputDebugStringW(psfd.GetResultString)
END IF

Template2

The following template implements the FileDialogEvents and IFileDialogControlEvents callback interfaces.

' ########################################################################################
' Microsoft Windows
' File: CW_CIOpenFileDialog_Events_02.bas
' Contents: Open file dialog with dialog and control events
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José© Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define _WIN32_WINNT &h0602
#define _COSFD_DEBUG_ 1
#include once "AfxNova/CWindow.inc"
#include once "AfxNova/AfxCOM.inc"
#include once "AfxNova/CIOpenSaveFile.inc"
#include once "AfxNova/CIFileDialogCustomize.inc"
#include once "AfxNova/CIFileDialogEvents.inc"
USING AfxNova

CONST IDC_RICHEDIT = 1001
CONST IDC_TEST = 1002

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   DIM pWindow AS CWindow
   DIM hWin AS HWND = pWindow.Create(NULL, "DisplayIOpenFile", @WndProc)
   pWindow.SetClientSize(800, 450)
   pWindow.Center

   ' // Add buttons
   pWindow.AddControl("Button", hWin, IDC_TEST, "&Test", pWindow.ClientWidth - 195, pWindow.ClientHeight - 35, 75, 23)
   pWindow.AddControl("Button", hWin, IDCANCEL, "&Close", pWindow.ClientWidth - 95, pWindow.ClientHeight - 35, 75, 23)
  
   ' // Anchor the controls
   pWindow.AnchorControl(IDC_TEST, AFX_ANCHOR_BOTTOM_RIGHT)
   pWindow.AnchorControl(IDCANCEL, AFX_ANCHOR_BOTTOM_RIGHT)

   ' // Dispatch Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ########################################################################################
' CIFileDialogControlEventsImpl class
' Implementation of the IFileDialogControlEvents callback interface
' ########################################################################################
TYPE CIFileDialogControlEventsImpl EXTENDS CIFileDialogControlEvents

   DECLARE FUNCTION OnItemSelected (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD, BYVAL dwIDItem AS DWORD) AS HRESULT
   DECLARE FUNCTION OnButtonClicked (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD) AS HRESULT
   DECLARE FUNCTION OnCheckButtonToggled (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD, BYVAL bChecked AS WINBOOL) AS HRESULT
   DECLARE FUNCTION OnControlActivating (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD) AS HRESULT

END TYPE

' // Override the virtual methods

' ========================================================================================
PRIVATE FUNCTION CIFileDialogControlEventsImpl.OnItemSelected (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD, BYVAL dwIDItem AS DWORD) AS HRESULT
   COSFD_DP("")
   RETURN S_OK
END FUNCTION
' ========================================================================================

' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION CIFileDialogControlEventsImpl.OnButtonClicked (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD) AS HRESULT
   COSFD_DP("")
   RETURN S_OK
END FUNCTION
' ========================================================================================

' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION CIFileDialogControlEventsImpl.OnCheckButtonToggled (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD, BYVAL bChecked AS WINBOOL) AS HRESULT
   COSFD_DP("")
   RETURN S_OK
END FUNCTION
' ========================================================================================

' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION CIFileDialogControlEventsImpl.OnControlActivating (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD) AS HRESULT
   COSFD_DP("")
   RETURN S_OK
END FUNCTION
' ========================================================================================

' ########################################################################################
' CIFileDialogEventsImpl class
' Implementation of the FileDialogEvents callback interface
' ########################################################################################
TYPE CIFileDialogEventsImpl EXTENDS CIFileDialogEvents

   DECLARE FUNCTION QueryInterface (BYVAL riid AS REFIID, BYVAL ppvObj AS LPVOID PTR) AS HRESULT
   DECLARE FUNCTION OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   DECLARE FUNCTION OnItemSelected (BYVAL pfdc AS IFileDialogCustomize PTR, BYVAL dwIDCtl AS DWORD, BYVAL dwIDItem AS DWORD) AS HRESULT

END TYPE

' ========================================================================================
' Returns pointers to the implemented classes and supported interfaces.
' ========================================================================================
PRIVATE FUNCTION CIFileDialogEventsImpl.QueryInterface (BYVAL riid AS REFIID, BYVAL ppvObj AS LPVOID PTR) AS HRESULT
   COSFD_DP(AfxGuidText(riid))
   IF ppvObj = NULL THEN RETURN E_INVALIDARG
   IF IsEqualIID(riid, @IID_IFileOpenDialog) OR _
      IsEqualIID(riid, @IID_IFileSaveDialog) OR _
      IsEqualIID(riid, @IID_IUnknown) THEN
      *ppvObj = @this
      ' // Increment the reference count
      this.AddRef
      RETURN S_OK
   ELSE
      IF IsEqualIID(riid, @IID_IFileDialogControlEvents) THEN
         ' // Create an instance of our implemented IFileDialogControlEvents interface
         DIM pIFileDialogControlEvents AS CIFileDialogControlEventsImpl PTR = NEW CIFileDialogControlEventsImpl
         ' // Increment the reference count
         pIFileDialogControlEvents->AddRef
         ' // Return a pointer to the event's class
         *ppvObj = CAST(ANY PTR, pIFileDialogControlEvents)
         RETURN S_OK
      END IF
   END IF
   RETURN E_NOINTERFACE
END FUNCTION
' =====================================================================================

PRIVATE FUNCTION CIFileDialogEventsImpl.OnFolderChange (BYVAL pfd AS IFileDialog PTR) AS HRESULT
   ' // Before displaying the dialog get its coordinates
   DIM pOleWindow AS IOleWindow PTR
   ' // Get a pointer to the IOleWindow interface
   pfd->lpvtbl->QueryInterface(pfd, @IID_IOleWindow, @pOleWindow)
   ' // Get the window handle of the dialog
   DIM hOleWindow AS HWND
   IF pOleWindow THEN pOleWindow->lpvtbl->GetWindow(pOleWindow, @hOleWindow)
   COSFD_DP ("hOleWindow: " & WSTR(hOleWindow))
   IF hOleWindow THEN
      ' // Get he bounding rectangle of the parent window
      DIM AS RECT rcDlg, rcOwner
      GetWindowRect(GetParent(hOleWindow), @rcOwner)
      ' // Get he bounding rectangle of the open file dialog
      GetWindowRect(hOleWindow, @rcDlg)
      ' // Maps the open file dialog coordinates relative to its parent window
      MapWindowPoints(GetParent(hOleWindow), hOleWindow, CAST(POINT PTR, @rcDlg), 2)
   END IF
   RETURN S_OK
END FUNCTION
' ########################################################################################

' ========================================================================================
' Main window callback procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

   STATIC hFocus AS HWND

   SELECT CASE uMsg

      CASE WM_NCACTIVATE
         ' // Save the handle of the control that has the focus
         IF wParam = 0 THEN hFocus = GetFocus
         ' // Note: Don't use EXIT FUNCTION

      CASE WM_SETFOCUS
         ' // Post a message to set the focus later, since some
         ' // Windows actions can steal it if we set it here
         IF hFocus THEN
            PostMessage hWnd, WM_USER + 999, cast(WPARAM, hFocus), 0
            hFocus = 0
         END IF

      CASE WM_USER + 999
         ' // Set the focus and show the line an column in the status bar
         IF wParam THEN SetFocus(cast(HWND, wParam))
         EXIT FUNCTION

      CASE WM_SYSCOMMAND
         ' // Capture this message and send a WM_CLOSE message
         IF (wParam AND &HFFF0) = SC_CLOSE THEN
            SendMessage hwnd, WM_CLOSE, 0, 0
            EXIT FUNCTION
         END IF

      CASE WM_COMMAND
         
         SELECT CASE GET_WM_COMMAND_ID(wParam, lParam)

            ' // If ESC key pressed, close the application sending an WM_CLOSE message
            CASE IDCANCEL
               IF GET_WM_COMMAND_CMD(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF

            ' // For testing purposes
            CASE IDC_TEST
               SCOPE
               DIM pofd AS CIOpenFileDialog
               ' // Set the file types
               pofd.AddFileType("FB code files", "*.bas;*.inc;*.bi")
               pofd.AddFileType("Executable files", "*.exe;*.dll")
               pofd.AddFileType("All files", "*.*")
               pofd.SetFileTypes()
               ' // Multiple selection (default is single selection)
               DIM options AS FILEOPENDIALOGOPTIONS = pofd.GetOptions
               pofd.SetOptions(options OR FOS_ALLOWMULTISELECT)
               ' // Optional: Set the title of the dialog
               '   pofd.SetTitle("A Single-Selection Dialog")
               ' // Set the folder
               pofd.SetFolder(CURDIR)
               pofd.SetDefaultExtension("bas")
               pofd.SetFileTypeIndex(1)
               ' // Customization
               ' // Get a pointer to the IFileDialog interface
               DIM pfdlg AS IFileDialog PTR = pofd.GetIFileDialogPtr
               IF pfdlg THEN
                  ' // Initialize an instance of the CIFileDialogCustomize class
                  DIM pCust AS CIFileDialogCustomize = pfdlg
                  ' // Relese the IFileDialog interface
                  pfdlg->lpvtbl->Release(pfdlg)
                  ' // Add controls to the dialog
                  IF pCust.GetPtr THEN
                     ' Call methods of the CIFileDialogCustomize class
                     pCust.AddCheckButton(1001, "My check button", TRUE)
                     pCust.AddComboBox(1002)
                     pCust.AddEditBox(1003, "My edit control")
                     pCust.AddMenu(1004, "My menu")
                     pCust.AddPushButton(1005, "My push button")
                     pCust.AddSeparator(1006)
                     pCust.AddText(1007, "My text")
                     pCust.EnableOpenDropDown(1008)
                  END IF
               END IF
               ' // Set events
               DIM pfde AS ANY PTR = NEW CIFileDialogEventsImpl
               pofd.SetEvents(pfde)
               ' // Display the dialog
               DIM hr AS HRESULT = pofd.ShowOpen(hwnd)
               ' // Folder name
               OutputDebugStringW("Folder name: " & pofd.GetFolder)
               ' *** Single selection ***
               ' // Get the result
               'IF hr = S_OK THEN
               '   OutputDebugStringW pofd.GetResult()
               'END IF
               ' *** Multiple selection ***
               IF hr = S_OK THEN
                  DIM dwsRes AS DWSTRING = pofd.GetResultsString
                  FOR i AS LONG = 1 TO pofd.GetResultsCount
                     OutputDebugStringW pofd.ParseResults(dwsRes, i)
                  NEXT
               END IF
               END SCOPE

         END SELECT

        CASE WM_DESTROY
         ' // End the application by sending an WM_QUIT message
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   ' // Default processing of Windows messages
   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================